package com.orange.framework.handler;

import com.orange.common.enums.ResultCodeEnum;
import com.orange.common.exception.*;
import com.orange.common.utils.RequestContextUtil;
import com.orange.framework.result.R;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * ClassName:GlobalExceptionHandler
 * Package:com.ywf.myproject.common.exception
 * Description:
 *
 * @Date:2021/2/28 18:02
 * @Author:YWF
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 通用异常处理方法
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e) {
        e.printStackTrace();
        return R.error();
    }

    /**
     * 空指针异常处理类
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public R error(NullPointerException e) {
        HttpServletRequest request = RequestContextUtil.getRequest();
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.NULL_POINT);
    }

    /**
     * Http状态码错误异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpClientErrorException.class)
    @ResponseBody
    public R error(HttpClientErrorException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.HTTP_CLIENT_ERROR);
    }

    /**
     * 请求方法不支持
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public R error(HttpRequestMethodNotSupportedException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.METHOD_NOT_SUPPORT);
    }

    /**
     * 请求body数据错误
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public R error(HttpMessageNotReadableException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.REQUEST_BODY_DATA_ERROR);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public R error(IllegalArgumentException e) {
        e.printStackTrace();
        return R.error().message(e.getMessage());
    }

    /**
     * 参数缺失
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public R error(MissingServletRequestParameterException e) {
        HttpServletRequest request = RequestContextUtil.getRequest();
        e.printStackTrace();
        return R.error().message("参数缺失：" + e.getParameterName());
    }

    @ExceptionHandler(BadSqlGrammarException.class)
    @ResponseBody
    public R error(BadSqlGrammarException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.ERROR_BAD_SQL);
    }

    /**
     * 未知主机
     *
     * @param e
     * @return
     */
    @ExceptionHandler(UnknownHostException.class)
    @ResponseBody
    public R error(UnknownHostException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.HTTP_UNKNOW_HOST);
    }

    /**
     * 请求参数校验错误
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public R error(ConstraintViolationException e) {
        List<R.Error> errors = e.getConstraintViolations().stream()
                .map(o -> {
                    String fieldArgs = o.getPropertyPath().toString();
                    String fileName = fieldArgs.substring(fieldArgs.lastIndexOf('.') + 1);
                    R.Error error = new R.Error(fileName, o.getMessage());
                    return error;
                })
                .distinct()  // 去除重复消息
                .collect(Collectors.toList());
        return R.setResult(ResultCodeEnum.PARAM_ERROR).errors(errors);
    }

    /**
     * 请求Body参数字段校验
     *
     * @param e
     * @return
     * @throws NoSuchFieldException
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public R error(MethodArgumentNotValidException e) {
        List<R.Error> errors = getErrors(e);
        return R.setResult(ResultCodeEnum.PARAM_ERROR).errors(errors);
    }

    @ExceptionHandler(BindException.class)
    @ResponseBody
    public R error(BindException e) {
        // 提取所有校验失败的干净错误消息
        List<R.Error> errors = getErrors(e);
        return R.setResult(ResultCodeEnum.PARAM_ERROR).errors(errors);
    }

    /**
     * 获取错误信息
     *
     * @param e
     * @return
     */
    private List<R.Error> getErrors(BindException e) {
        return e.getBindingResult().getAllErrors()
                .stream()
                .map(o -> {
                    String fieldName = "";
                    // 处理字段级错误（FieldError）
                    if (o instanceof FieldError) {
                        fieldName = ((FieldError) o).getField();
                    }
                    // 处理全局错误（ObjectError）
                    else if (o instanceof ObjectError) {
                        fieldName = "global"; // 标记为全局错误
                    }
                    R.Error error = new R.Error(fieldName, o.getDefaultMessage());
                    return error;
                })
                .distinct() // 去除重复消息
                .collect(Collectors.toList());
    }


    /**
     * 自定义业务异常处理类
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ApiSecurityException.class)
    @ResponseBody
    public R error(ApiSecurityException e) {
        e.printStackTrace();
        return R.error().message(e.getMessage()).code(e.getCode());
    }

    /**
     * 自定义业务异常处理类
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public R error(BusinessException e) {
        e.printStackTrace();
        return R.error().message(e.getMessage()).code(e.getCode());
    }

    /**
     * 自定义登录异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(UserLoginException.class)
    @ResponseBody
    public R error(UserLoginException e) {
        e.printStackTrace();
        return R.error().message(e.getMessage()).code(e.getCode());
    }

    /**
     * 用户工具类调用异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(InvokeException.class)
    @ResponseBody
    public R error(InvokeException e) {
        e.printStackTrace();
        return R.error().message(e.getMessage()).code(e.getCode());
    }

    @ExceptionHandler(AsyncRequestTimeoutException.class)
    @ResponseBody
    public R error(AsyncRequestTimeoutException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.SYNC_REQUEST_TIMEOUT);
    }

    @ExceptionHandler(AgentApiException.class)
    @ResponseBody
    public R error(AgentApiException e) {
        // 提取所有校验失败的干净错误消息
        List<R.Error> errors = new ArrayList<>();
        errors.add(new R.Error(String.valueOf(e.getCode()), e.getMessage()));
        return R.setResult(ResultCodeEnum.AGENT_HTTP_ERROR).errors(errors);
    }

}
